/**
 * 
 */
package com.maohi.software.arii.modules;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.maohi.software.arii.Arii;
import com.maohi.software.arii.interfaces.ExtensionContext;
import com.maohi.software.arii.jaxb2.Plugin.ExtensionPoints.ExtensionPoint;
import com.maohi.software.arii.jaxb2.Plugin.Extensions.Extension;
import com.maohi.software.arii.jaxb2.Plugin.Extensions.Extension.Element;
import com.maohi.software.arii.jaxb2.Plugin.Extensions.Extension.Element.Context;
import com.maohi.software.arii.utils.Keys;

/**
 * @author heifara
 * 
 */
public class ExtensionModule extends AbstractModule {

	private static class ListExtension implements Provider<List<?>> {
		private static class ExtensionContextMap extends HashMap<String, Object> implements ExtensionContext {
			private static final long serialVersionUID = 1L;

			@Override
			public Object getValue(String aKey) {
				return this.get(aKey);
			}

		}

		private static class ExtensionContextModule extends AbstractModule {

			private ExtensionContext context;

			public ExtensionContextModule(ExtensionContext aContext) {
				this.context = aContext;
			}

			@Override
			protected void configure() {
				this.bind(ExtensionContext.class).toInstance(this.context);
			}

		}

		private static Logger logger = Logger.getLogger(ExtensionModule.ListExtension.class.getName());

		private List<Extension> extensions;
		private ClassLoader classloader;

		public ListExtension(List<Extension> aExtensions, ClassLoader aClassLoader) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
			this.extensions = aExtensions;
			this.classloader = aClassLoader;
		}

		@Override
		public List<Object> get() {
			List<Object> iResults = new ArrayList<Object>();

			try {
				List<Module> iModules = Arii.getInstance(Keys.getListModule());
				for (Extension iExtension : this.extensions) {
					for (Element iElement : iExtension.getElement()) {
						Class<? extends Object> iClass = Class.forName(iElement.getClazz(), true, this.classloader);

						ExtensionContextMap iExtensionContext = new ExtensionContextMap();
						for (Context iContext : iElement.getContext()) {
							iExtensionContext.put(iContext.getKey(), iContext.getValue());
						}

						iModules.add(new ExtensionContextModule(iExtensionContext));
						Injector injector = Guice.createInjector(iModules);

						Object iObject = injector.getInstance(iClass);

						iResults.add(iObject);
					}
				}
			} catch (Exception aException) {
				logger.error(aException.getMessage(), aException);
			}

			return iResults;
		}
	}

	private static Logger logger = Logger.getLogger(ExtensionModule.class.getName());

	@SuppressWarnings("unchecked")
	@Override
	protected void configure() {
		long iStart = System.currentTimeMillis();

		Map<ExtensionPoint, List<Extension>> iExtensionPoints = Arii.getInstance(Keys.getMapExtensionPointListExtension());
		ClassLoader iClassLoader = Arii.getInstance(ClassLoader.class);

		try {
			for (ExtensionPoint iExtensionPoint : iExtensionPoints.keySet()) {
				Class<?> iClass = Class.forName(iExtensionPoint.getClazz(), false, iClassLoader);
				String iId = iExtensionPoint.getId();
				this.bind(Keys.getListExtension(iClass, iId)).toProvider(new ListExtension(iExtensionPoints.get(iExtensionPoint), iClassLoader));
			}
		} catch (Exception aException) {
			logger.error(aException.getMessage(), aException);
		} finally {
			logger.debug(String.format("time: %d", System.currentTimeMillis() - iStart));
		}
	}
}
